//============================================================================
// Name        : aller.cpp
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include "go/GoGame.h"
#include <stdio.h>
#include <stdlib.h>  /* for rand() and srand() */
#include <string>
#include "gtpengine/gtp.h"
#include "smartgame/SgInit.h"
#include "smartgame/SgDebug.h"
#include "evaluation/Evaluation.h"
//#include "mc/mcMain.h"

/* Forward declarations. */
static int gtp_protocol_version(char *s);
static int gtp_name(char *s);
static int gtp_version(char *s);
static int gtp_known_command(char *s);
static int gtp_list_commands(char *s);
static int gtp_quit(char *s);
static int gtp_boardsize(char *s);
static int gtp_clear_board(char *s);
static int gtp_komi(char *s);
static int gtp_fixed_handicap(char *s);
static int gtp_place_free_handicap(char *s);
static int gtp_set_free_handicap(char *s);
static int gtp_play(char *s);
static int gtp_genmove(char *s);
static int gtp_final_score(char *s);
static int gtp_final_status_list(char *s);
static int gtp_showboard(char *s);
static int board_size = 13;
//static log4cpp::Category* log;
static GoGame* game;
void test();
void test1();
void evaTest();
void testmc();

/* List of known commands. */
static struct gtp_command commands[] = { { "protocol_version",
		gtp_protocol_version }, { "name", gtp_name },
		{ "version", gtp_version }, { "known_command", gtp_known_command }, {
				"list_commands", gtp_list_commands }, { "quit", gtp_quit }, {
				"boardsize", gtp_boardsize },
		{ "clear_board", gtp_clear_board }, { "komi", gtp_komi }, {
				"fixed_handicap", gtp_fixed_handicap }, {
				"place_free_handicap", gtp_place_free_handicap }, {
				"set_free_handicap", gtp_set_free_handicap }, { "play",
				gtp_play }, { "genmove", gtp_genmove }, { "final_score",
				gtp_final_score },
		{ "final_status_list", gtp_final_status_list }, { "showboard",
				gtp_showboard }, { NULL, NULL } };

#define NDEBUG

int main(int argc, char **argv) {
//	testmc();
	unsigned int random_seed = 1;

	/* Optionally a random seed can be passed as an argument to the program. */
	if (argc > 1)
		sscanf(argv[1], "%u", &random_seed);
	srand(random_seed);

	/* Make sure that stdout is not block buffered. */
	setbuf(stdout, NULL);

	/* Inform the GTP utility functions about the initial board size. */
	gtp_internal_set_boardsize(13);

	/* Initialize the board. */
	SgInit();
	game = new GoGame();
	//	log = getLogger("aller");
	//	test();
	//test1();
	/* Process GTP commands. */
	SgDebug() << "main loop\n";
	gtp_main_loop(commands, stdin, NULL);
//	testmc();
	return 0;
}

void test() {
	// black
	SgPoint p1 = SgPointUtil::Pt(2, 1);
	SgPoint p2 = SgPointUtil::Pt(2, 3);
	SgPoint p3 = SgPointUtil::Pt(1, 2);
	SgPoint p4 = SgPointUtil::Pt(3, 2);
	// white
	SgPoint p5 = SgPointUtil::Pt(3, 1);
	SgPoint p6 = SgPointUtil::Pt(3, 3);
	SgPoint p7 = SgPointUtil::Pt(4, 2);
	SgPoint p8 = SgPointUtil::Pt(2, 2);
	game->play(p1);
	game->play(p5);
	game->play(p2);
	game->play(p6);
	game->play(p3);
	game->play(p7);
	game->play(p4);
	game->play(p8);
	game->play(p4);

	//game->addMove(m4);
	//game->addMove(m5);
	//	SgDebug() << SgPointUtil::PointToString(p2) << '\n';
	//	SgDebug() << SgPointUtil::PointToString(p3) << '\n';
	//Board b;
}

void test1() {
	SgPoint p1 = SgPointUtil::Pt(4, 4);
	SgPoint p2 = SgPointUtil::Pt(7, 7);
	SgPointSet set;
	set.Include(p1);
	set.Include(p2);
	SgPointSet area = set.Border(SG_MAX_SIZE);
	SgDebug() << p1 << " " << p2 << "\n";
	for (SgSetIterator sit(area); sit; ++sit)
		SgDebug() << *sit << " ";
	SgDebug() << "\n";
}

//void testmc() {
//	Board board;
//	RegionBoard rb(board);
//	mcMain mc;
//	for(int i = 0; i < 10; i++) {
//		Move m = mc.nextMove(rb);
//		board.play(m);
//		rb.onExecMove(m);
//	}
//}

/* We are talking version 2 of the protocol. */
static int gtp_protocol_version(char *s) {
	return gtp_success("2");
}

static int gtp_name(char *s) {
	return gtp_success("Aller");
}

static int gtp_version(char *s) {
	return gtp_success("0");
}

static int gtp_known_command(char *s) {
	int i;
	char command_name[GTP_BUFSIZE];

	/* If no command name supplied, return false (this command never
	 * fails according to specification).
	 */
	if (sscanf(s, "%s", command_name) < 1)
		return gtp_success("false");

	for (i = 0; commands[i].name; i++)
		if (!strcmp(command_name, commands[i].name))
			return gtp_success("true");

	return gtp_success("false");
}

static int gtp_list_commands(char *s) {
	int i;

	gtp_start_response(GTP_SUCCESS);

	for (i = 0; commands[i].name; i++)
		gtp_printf("%s\n", commands[i].name);

	gtp_printf("\n");
	return GTP_OK;
}

static int gtp_quit(char *s) {
	gtp_success("");
	return GTP_QUIT;
}

static int gtp_boardsize(char *s) {
	return gtp_success("");
}

static int gtp_clear_board(char *s) {
	SgDebug() << "clear board\n";
	game->init();
	return gtp_success("");
}

static int gtp_komi(char *s) {
	float komi;
	if (sscanf(s, "%f", &komi) < 1)
		return gtp_failure("komi not a float");

	game->setKomi(komi);
	return gtp_success("");
}

/* Common code for fixed_handicap and place_free_handicap. */
static int place_handicap(char *s, int fixed) {/*
 int handicap;
 int m, n;
 int first_stone = 1;

 if (!board_empty())
 return gtp_failure("board not empty");

 if (sscanf(s, "%d", &handicap) < 1)
 return gtp_failure("handicap not an integer");

 if (handicap < 2)
 return gtp_failure("invalid handicap");

 if (fixed && !valid_fixed_handicap(handicap))
 return gtp_failure("invalid handicap");

 if (fixed)
 place_fixed_handicap(handicap);
 else
 place_free_handicap(handicap);

 gtp_start_response(GTP_SUCCESS);
 for (m = 0; m < board_size; m++)
 for (n = 0; n < board_size; n++)
 if (get_board(m, n) != EMPTY) {
 if (first_stone)
 first_stone = 0;
 else
 gtp_printf(" ");
 gtp_mprintf("%m", m, n);
 }*/
	return gtp_finish_response();
}

static int gtp_fixed_handicap(char *s) {
	return place_handicap(s, 1);
}

static int gtp_place_free_handicap(char *s) {
	return place_handicap(s, 0);
}

static int gtp_set_free_handicap(char *s) {
	return gtp_success("");
}

static void gtp2sg(int& c, int& r) {
	r = 13 - r;
	c++;
}

static void sg2gtp(int& c, int& r) {
	r = 13 - r;
	c--;
}

static int gtp_play(char *s) {
	int row, column;
	int color = SG_EMPTY;

	if (!gtp_decode_move(s, &color, &row, &column))
		return gtp_failure("invalid color or coordinate");

	if (column == -1 && row == -1){
		Move *m = new Move(color, SG_PASS);
		game->addMove(*m);
	}
	else{
		gtp2sg(column, row);
		SgPoint p = SgPointUtil::Pt(column, row);
		Move *m = new Move(color, p);
		game->addMove(*m);
	}
	return gtp_success("");
}

static int gtp_genmove(char *s) {
	int color = SG_EMPTY;

	if (!gtp_decode_color(s, &color))
		return gtp_failure("invalid color");

	//  generate_move(&i, &j, color);
//	SgDebug()<<"gen move   \n";
	Move m = game->nextMove();
//	if (m == SG_NULLMOVE)
	int col, row;
//	SgDebug()<<"add move   "<< SgBW(m.Color())  << "  "<< SgWritePoint(m.Point())<<"\n";
	game->addMove(m);
	if (m.Point() == SG_PASS){
		col = -1;
		row = -1;
	}
	else{
		SgPoint p = m.Point();
		col = SgPointUtil::Col(p);
		row = SgPointUtil::Row(p);
		sg2gtp(col, row);
	}
	//play_move(i, j, m.Color());
	gtp_start_response(GTP_SUCCESS);
	gtp_mprintf("%m", row, col);
	return gtp_finish_response();
}

/* Compute final score. We use area scoring since that is the only
 * option that makes sense for this move generation algorithm.
 */
static int gtp_final_score(char *s) {
	float score =  -3.14;
	score += Evaluation(RegionBoard(game->getBoard())).value();
	if (score > 0.0)
		return gtp_success("W+%3.1f", score);
	if (score < 0.0)
		return gtp_success("B+%3.1f", -score);
	return gtp_success("0");
}

static int gtp_final_status_list(char *s) {
	int n;
	int status = 0;
	char status_string[GTP_BUFSIZE];
	int first_string;

	if (sscanf(s, "%s %n", status_string, &n) != 1)
		return gtp_failure("missing status");

	if (!strcmp(status_string, "alive"))
		status = 1;
	else if (!strcmp(status_string, "dead"))
		status = 2;
	else if (!strcmp(status_string, "seki"))
		status = 3;
	else
		return gtp_failure("invalid status");

	//  compute_final_status();

	gtp_start_response(GTP_SUCCESS);

	first_string = 1;
	/*
	 for (i = 0; i < board_size; i++)
	 for (j = 0; j < board_size; j++)
	 if (get_final_status(i, j) == status) {
	 int k;
	 int stonei[SG_MAX_SIZE * SG_MAX_SIZE];
	 int stonej[SG_MAX_SIZE * SG_MAX_SIZE];
	 int num_stones = get_string(i, j, stonei, stonej);
	 Clear the status so we don't find the string again.
	 for (k = 0; k < num_stones; k++)
	 set_final_status(stonei[k], stonej[k], UNKNOWN);

	 if (first_string)
	 first_string = 0;
	 else
	 gtp_printf("\n");

	 gtp_print_vertices(num_stones, stonei, stonej);
	 }
	 */
	return gtp_finish_response();
}

/* Write a row of letters, skipping 'I'. */
static void letters(void) {
	int i;

	printf("  ");
	for (i = 0; i < board_size; i++)
		printf(" %c", 'A' + i + (i >= 8));
}

static int gtp_showboard(char *s) {
	int i, j;
	int symbols[3] = { '.', 'O', 'X' };

	gtp_start_response(GTP_SUCCESS);
	gtp_printf("\n");

	letters();

	for (i = 0; i < board_size; i++) {
		printf("\n%2d", board_size - i);

		for (j = 0; j < board_size; j++) {
			//			printf(" %c", symbols[(i, j)]);
		}

		printf(" %d", board_size - i);
	}

	printf("\n");
	letters();
	return gtp_finish_response();
}

/*
 * Local Variables:
 * tab-width: 8
 * c-basic-offset: 2
 * End:
 */

void evaTest() {
	//Board:
	//+++++++++++++
	//++++++o+o++++
	//++xxxxo++x+x+
	//++xoooxo+++++
	//++o+++x++++o+
	//+++o+++++++++
	//++++++x+++o++
	//+++o++++++o++
	//++x++++++x+++
	//++++++++x+o++
	//+++x+ox++++++
	//+++++++++++++
	//+++++++++++++
	//Board:
	//+xx++++o+++++
	//+xx+++ooo++++
	//++xxxxo++x+x+
	//++xoooxo++o++
	//++oo++x++x+o+
	//+++o++++x++oo
	//++++++x+++ooo
	//+++o++++++ooo
	//+xx+++++xx+++
	//xxx+x+++xxo++
	//xxxx+ox++++++
	//xxxx+++++o+++
	//xxxxx+++++++x

	SgInit();
	game = new GoGame();
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(4, 3)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(6, 3)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(7, 3)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(11, 4)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(9, 4)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(4, 6)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(10, 5)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(11, 7)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(7, 9)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(3, 9)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(3, 10)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(4, 10)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(4, 11)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(6, 10)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(6, 11)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(7, 12)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(12, 11)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(11, 6)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(3, 5)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(4, 8)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(7, 7)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(12, 9)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(7, 10)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(5, 10)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(3, 11)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(7, 11)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(5, 11)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(8, 10)));
	game->addMove(Move(SG_BLACK, SgPointUtil::Pt(10, 11)));
	game->addMove(Move(SG_WHITE, SgPointUtil::Pt(9, 12)));
	RegionBoard reg(game->getBoard());
	Evaluation(reg).value();
}

void testmc() {
//	SgInit();
//	game = new GoGame();
//	mcMain mc;
//	for(int i = 0; i < 10; i++) {
//		SgDebug()<<"i:  "<<i<<"\n";
//		RegionBoard reg(game->getBoard());
//		SgDebug()<<"i:  "<<&reg<<"\n";
//		Move m = mc.nextMove(reg);
//		SgDebug()<<m<<"\n";
//		game->addMove(m);
//	}
}
